<?php

require_once "Service/DataConnection.php";

class Service_Model
{
    protected static $_defaultEncoding = 'UTF-8';

    public static function setDefaultEncoding($encoding)
    {
        self::$_defaultEncoding = $encoding;
    }

    public static function getDefaultEncoding()
    {
        return self::$_defaultEncoding;
    }

    /**
     * @var Service_DataSource
     */
    protected $_ds = null;

    protected $_spec = null;
    protected $_identifier = null;
    protected $_encoding = null;
    
    protected $_filters = array();

    /**
     * Constructor.
     *
     * @param Service_DataSource|string $ds
     */
    public function __construct($ds = null, $encoding = null)
    {
        if (null === $ds) {
             $ds = $this->_ds;
        }

        $this->setDataSource($ds);

        if (null === $encoding) {
            $this->_encoding = $this->getDefaultEncoding();
        }

        $this->init();
    }

    /**
     * Initializes a model.
     */
    public function init()
    {      
    }

    /**
     * Sets a data source.
     *
     * @param Service_DataSource|string $ds
     * @returns Service_Model
     */
    public function setDataSource($ds)
    {
        if (!($ds instanceof Service_DataSource)) {
            $ds = (string) $ds;
            if (!empty($ds)) {
                $ds = Service_DataConnection::getConnection($ds);
            } else {
                $ds = null;
            }
        }

        $this->_ds = $ds;
        
        return $this;
    }

    public function getDataSource() 
    {
        return $this->_ds;
    }

    public function getSpec()
    {
        return $this->_spec;
    }

    public function getIdentifier() 
    {
        $spec = $this->getSpec();
        return $spec["identifier"];
    }
    public function getFilters()
    {
        return $this->_filters;
    }

    public function getItem($id, $attrList = null)
    {
        $spec = $this->getSpec();
        return $this->_ds->getItem($this->getSpec(), $this->_prepareId($id),
            $attrList);
    }

    public function itemExists($id)
    {
        $identifier = $this->getIdentifier();
        return (boolean) $this->getItem($id, array($identifier));
    }

    public function putItem($id, $item)
    {
        return $this->_ds->putItem($this->getSpec(),
            $id ? $this->_prepareId($id) : $id, $this->_checkEncoding($item));
    }
    
    public function updateItems($fields)
    {
        return $this->_ds->updateItems($this->getSpec(),
            $this->_checkEncoding($fields), $this->getFilters());
    }

    public function deleteItem($id)
    {
        return $this->_ds->removeItem($this->getSpec(), $this->_prepareId($id));
    }
    
    public function removeItems()
    {
        return $this->_ds->removeItems($this->getSpec(), $this->getFilters());
    }

    public function listItems($attrList = null, $limit = 100, $offset = 0, $sort = null)
    {
        return $this->_ds->listItems($this->getSpec(), $attrList, $limit, $offset, $sort, $this->getFilters());
    }
    
    public function countItems()
    {
        return $this->_ds->countItems($this->getSpec(), $this->getFilters());
    }

    protected function _prepareId($id)
    {
        if ($id) {
            $identifier = $this->getIdentifier();
            return array($identifier => $id);
        }
        return $id;
    }

    /**
     * Checks fields' encoding.
     *
     * @param arrays $fields fields to validate
     * @return array
     * @throws Service_Resource_InvalidParameterEncodingException
     */
    protected function _checkEncoding($fields)
    {
        mb_internal_encoding($this->_encoding);

        foreach ($fields as $name => $field) {
            if (is_string($field)) {
                
                if (!mb_check_encoding($field)) {
                    // TODO make model exceptions and handle them outside
                    require_once 'Service/Resource/InvalidParameterEncodingException.php';
                    throw new Service_Resource_InvalidParameterEncodingException(
                        "Parameter '{$name}' has wrong encoding."
                        . " System uses '{$this->_encoding}'.");
                } elseif (preg_match('/[\x00-\x08\x0B\x0C\x0E-\x1F]/', $field)) {
                    // reject ASCII 0-8, 11, 12, 14-31
                    
                    // TODO check this outside (it's just an XML workaround)
                    require_once 'Service/Resource/InvalidParameterEncodingException.php';
                    throw new Service_Resource_InvalidParameterEncodingException(
                        "Parameter '{$name}' contains characters not supported"
                        . " for '{$this->_encoding}' encoding.");
                }
            }
        }

        return $fields;
    }

}
